package com.dave.admin.config.auth;

import com.dave.admin.config.auth.component.*;
import com.dave.admin.config.auth.service.BossUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

/**
 * Spring Security 认证配置
 *
 * @author Dave
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 自己的登录路径
     */
    public static final String ADMIN_LOGIN = "/login";

    /**
     * 自己的退出路径
     */
    public static final String ADMIN_LOGOUT = "/logout";

    /**
     * 没有登录处理
     */
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    /**
     * 没有权限处理
     */
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    /**
     * 登录失败处理
     */
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    /**
     * 登录成功处理
     */
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    /**
     * 用户信息处理
     */
    @Autowired
    private BossUserDetailsService bossUserDetailsService;
    /**
     * 退出自定义逻辑
     */
    @Autowired
    private BossLogoutHandler bossLogoutHandler;
    /**
     * 退出成功处理
     */
    @Autowired
    private RestLogoutSuccessHandler restLogoutSuccessHandler;

    /**
     * 放行策略
     */
    @Autowired
    private PermitAllUrlConfiguration permitAllUrl;

    /**
     * 配置请求设置
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        permitAllUrl.getUrls().stream().forEach(url -> registry.antMatchers(url).permitAll());

        //所有请求都得鉴权,
        registry.anyRequest().authenticated()
                // 由于使用的是token，我们这里不需要csrf也不需要session
                .and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //允许跨域访问
        httpSecurity.cors();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationTokenFilter(), CustomAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationTokenFilter(), LogoutFilter.class)
                .logout()
                .logoutUrl(ADMIN_LOGOUT)
                .addLogoutHandler(bossLogoutHandler)
                .logoutSuccessHandler(restLogoutSuccessHandler)
                .clearAuthentication(true);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }


    /**
     * 注册自定义的UsernamePasswordAuthenticationFilter
     */
    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(loginSuccessHandler);
        filter.setAuthenticationFailureHandler(loginFailureHandler);
        filter.setFilterProcessesUrl(ADMIN_LOGIN);

        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

    /**
     * 用户认证
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(daoAuthenticationProvider());
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    /**
     * 数据库形式
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(bossUserDetailsService);
        // 使用BCrypt进行密码的hash
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }
}
